#pragma once

#include "../mq_comm/mq_helper.hpp"
#include "../mq_comm/mq_log.hpp"
#include "../mq_comm/mq_msg.pb.h"

#include <iostream>
#include <unordered_map>
#include <mutex>
#include <vector>
#include <string>
#include <memory>

namespace mq
{
    using namespace ns_helper;
    struct Binding
    {
        using ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string msgqueue_name;
        std::string binding_key; // 分发匹配规则-决定了哪些数据能被交换机放⼊队列

        Binding() {}
        Binding(const std::string &ename, const std::string &qname, const std::string &key) : exchange_name(ename), msgqueue_name(qname), binding_key(key)
        {
        }
    };
    // 队列与binding的映射关系 (队列与bind信息是一一对应的)
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    // 交换机名称与队列的映射关系 (一个交换机可以有多个bind信息)
    // 这里的队列信息是一个map, 一个交换机可以与多个队列进行绑定;
    using ExchangeBindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
    class BindingMapper
    {
    public:
        BindingMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);
            _sql_helper.open();
            CreateTable();
        }
        void CreateTable()
        {
            std::stringstream sql;
            sql << "create table if not exists binding_table(";
            sql << "exchange_name varchar(32), ";
            sql << "msgqueue_name varchar(32), ";
            sql << "binding_key varchar(128));";

            assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
        }
        void RemoveTable()
        {
            std::stringstream sql;
            sql << "drop table if exists binding_table";
            _sql_helper.exec(sql.str(), nullptr, nullptr);
        }

        bool insert(Binding::ptr &binding)
        {
            std::stringstream sql;
            sql << "insert into binding_table values(";
            sql << "'" << binding->exchange_name << "', ";
            sql << "'" << binding->msgqueue_name << "', ";
            sql << "'" << binding->binding_key << "');";
            return _sql_helper.exec(sql.str(), nullptr, nullptr);
        }

        // 移除指定bind信息
        void Remove(const std::string &ename, const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name='" << ename << "' and ";
            sql << "msgqueue_name='" << qname << "';";
            _sql_helper.exec(sql.str(), nullptr, nullptr);
        }

        // 移除交换机相关bind数据绑定信息
        void RemoveExchangeBindings(const std::string &ename)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name='" << ename << "';";
            _sql_helper.exec(sql.str(), nullptr, nullptr);
        }

        // 移除指定队列相关绑定信息
        void RemoveMsgQueueBindings(const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "msgqueue_name='" << qname << "';";
            _sql_helper.exec(sql.str(), nullptr, nullptr);
        }

        ExchangeBindingMap Recovery()
        {
            ExchangeBindingMap res;
            std::string sql = "select exchange_name, msgqueue_name, binding_key from binding_table;";
            _sql_helper.exec(sql, selectCallBack, &res);
            return res;
        }

    private:
        static int selectCallBack(void *arg, int numcol, char **row, char **fields)
        {
            ExchangeBindingMap *res = (ExchangeBindingMap *)arg;

            Binding::ptr bp = std::make_shared<Binding>(row[0], row[1], row[2]);
            // 为了防止 交换机相关的绑定信息已经存在，因此不能直接创建队列映射，进行添加，这样会覆盖历史数据
            // 因此得先获取交换机对应的映射对象，往里边添加数据
            // 但是，若这时候没有交换机对应的映射信息，因此这里的获取要使用引用（会保证不存在则自动创建）
            // 为了防止 交换机映射的MsgQueueBindingMap被覆盖
            MsgQueueBindingMap &qmap = (*res)[bp->exchange_name];
            qmap.insert(std::make_pair(bp->msgqueue_name, bp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &dbfile) : _mapper(dbfile)
        {
            _ebindings = _mapper.Recovery();
        }
        bool Bind(const std::string &ename, const std::string &qname, const std::string key, bool durable)
        {
            // 加锁, 构建一个队列的绑定信息对象, 添加映射关系
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _ebindings.find(ename);
            if (it != _ebindings.end() && it->second.find(qname) != it->second.end())
            {
                return true;
            }
            // 绑定信息是否需要持久化，取决于什么？ 交换机数据是持久化的，以及队列数据也是持久化的。
            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
            if (durable)
            {
                bool ret = _mapper.insert(bp);
                if (ret == false)
                    return false;
            }
            auto &qbmap = _ebindings[ename];

            qbmap.insert(std::make_pair(qname, bp));
            return true;
        }
        void UnBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto eit = _ebindings.find(ename);
            if (eit == _ebindings.end())
            {
                return; // 没有交换机相关的绑定信息
            }
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
            {
                return;
            }
            _mapper.Remove(ename, qname);
            _ebindings[ename].erase(qname);
        }
        void RemoveExchangeBinding(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _mapper.RemoveExchangeBindings(ename);
            _ebindings.erase(ename);
        }
        void RemoveMsgQueueBinding(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _mapper.RemoveMsgQueueBindings(qname);
            for (auto &start : _ebindings) // 注意这里要是有 &, 不使用& 删除时是 _ebindings内容的拷贝
            {
                // 遍历每个交换机的绑定信息，从中移除指定队列的相关信息
                start.second.erase(qname);
            }
            // for (auto start = _ebindings.begin(); start != _ebindings.end(); ++start)
            // {
            //     // 遍历每个交换机的绑定信息，从中移除指定队列的相关信息
            //     start->second.erase(qname);
            // }
        }
        MsgQueueBindingMap getExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto eit = _ebindings.find(ename);
            if (eit == _ebindings.end())
                return MsgQueueBindingMap();
            return eit->second;
        }
        Binding::ptr getBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto eit = _ebindings.find(ename);
            if (eit == _ebindings.end())
                return Binding::ptr();

            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return Binding::ptr();

            return qit->second;
        }
        bool IsExists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto eit = _ebindings.find(ename);
            if (eit == _ebindings.end())
                return false;

            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return false;

            return true;
        }
        // 返回所有的绑定信息数量
        size_t Size()
        {
            size_t total_size = 0;
            std::unique_lock<std::mutex> lock(_mtx);
            for (auto start = _ebindings.begin(); start != _ebindings.end(); ++start)
            {
                // 遍历每个交换机的绑定信息，从中移除指定队列的相关信息
                total_size += start->second.size();
            }
            return total_size;
        }
        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _mapper.RemoveTable();
            _ebindings.clear();
        }

    private:
        std::mutex _mtx;
        BindingMapper _mapper;
        ExchangeBindingMap _ebindings;
    };
}